using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Microblaze.Simulator.Engine;

namespace Weazel.Microblaze.Simulator.Components.Microblaze.Instructions
{
    /// <summary>
    /// Arithmetic Add
    /// </summary>
    public class Add : Instruction
    {
        public enum AddInstructionKind
        {
            None,  // Error
            addkc, // add with carry, keep carry
            addc,  // add with carry
            addk,  // add keep carry
            add
        }

        private AddInstructionKind kind;

        public override int CycleCount
        {
            get { return 1; }
        }

        public Add()
            : base("add")
        {
        }

        public override void Set(uint address, uint value)
        {
            base.Set(address, value);

            this.kind =
              (Opcode & 0x06) == 0x06 ? AddInstructionKind.addkc :
              (Opcode & 0x06) == 0x02 ? AddInstructionKind.addc :
              (Opcode & 0x06) == 0x04 ? AddInstructionKind.addk :
              (Opcode & 0x06) == 0x00 ? AddInstructionKind.add :
               AddInstructionKind.None;

            if (kind == AddInstructionKind.None)
                throw new UnknownInstructionException(value);
        }

        /// <summary>
        /// The sum of the contents of registers rA and rB, is placed into register rD. 
        /// Bit 3 of the instruction (labeled as K in the figure) is set to a one for 
        /// the mnemonic addk. Bit 4 of the instruction (labeled as C in the figure) is 
        /// set to a one for the mnemonic addc. Both bits are set to a one for the 
        /// mnemonic addkc. When an add instruction has bit 3 set (addk, addkc), the 
        /// carry flag will Keep its previous value regardless of the outcome of the 
        /// execution of the instruction. If bit 3 is cleared (add, addc), then the 
        /// carry flag will be affected by the execution of the instruction. When bit 4 
        /// of the instruction is set to a one (addc, addkc), the content of the carry 
        /// flag (MSR[C]) affects the execution of the instruction. When bit 4 is 
        /// cleared (add, addk), the content of the carry flag does not affect the 
        /// execution of the instruction (providing a normal addition).
        /// </summary>
        public override void Execute(MicroblazeComponent MicroblazeComponent)
        {
            bool C =
              kind == AddInstructionKind.addc ||
              kind == AddInstructionKind.addkc;

            bool K =
              kind == AddInstructionKind.addk ||
              kind == AddInstructionKind.addkc;

            uint ra = MicroblazeComponent.ReadRegister(SourceRegisterA);
            uint rb = MicroblazeComponent.ReadRegister(SourceRegisterB);

            ulong rd = 0; // the result

            if (!C)
            {
                rd = (ulong)ra + (ulong)rb;
            }
            else
            {
                uint carry = MicroblazeComponent.ArithmeticCarry ? (uint)1 : (uint)0;
                rd = (ulong)ra + (ulong)rb + (ulong)carry;
            }

            // store result
            MicroblazeComponent.WriteRegister(DestinationRegister, (uint)rd, Address);

            if (!K)
            {
                if (rd > 0xFFFFFFFF)
                    MicroblazeComponent.ArithmeticCarry = true;
                else
                    MicroblazeComponent.ArithmeticCarry = false;
            }
        }

        public override string ToString()
        {
            return string.Format("{0} r{1}, r{2}, r{3}",
              kind,
              DestinationRegister,
              SourceRegisterA,
              SourceRegisterB
            );
        }
    }
}
